package com.pig4cloud.pig.gateway.filter;

import cn.hutool.core.util.StrUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.pig4cloud.pig.common.core.constant.SecurityConstants;
import com.pig4cloud.pig.common.core.enums.LoginTypeEnum;
import com.pig4cloud.pig.common.core.exception.ValidateCodeException;
import com.pig4cloud.pig.common.core.util.R;
import com.pig4cloud.pig.common.core.util.WebUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.rewrite.CachedBodyOutputMessage;
import org.springframework.cloud.gateway.support.BodyInserterContext;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * 数据过滤
 *
 * @author lw
 */
@Component
public class HttpGatewayFilter {

    /**
     * @author lw
     * @date 2020/7/22
     * <p>
     * 自定义basic认证，针对特殊场景使用
     */
    @Component
    class HttpBasicGatewayFilter extends BaseAbstractGatewayFilter {

        @Override
        Mono<Void> applys(ServerWebExchange exchange, GatewayFilterChain chain, ServerHttpResponse response, ServerHttpRequest request) {
            if (hasAuth(exchange)) {
                return chain.filter(exchange);
            } else {
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                response.getHeaders().add(HttpHeaders.WWW_AUTHENTICATE, "Basic Realm=\"pig\"");
                return response.setComplete();
            }
        }
    }

    /**
     * @author lw
     * @date 2020/7/22
     * 演示环境过滤处理
     */
    @Slf4j
    @Component
    static class PreviewGatewayFilter extends BaseAbstractGatewayFilter {

        @Override
        Mono<Void> applys(ServerWebExchange exchange, GatewayFilterChain chain, ServerHttpResponse response, ServerHttpRequest request) {
            // GET，直接向下执行
            if (StrUtil.equalsIgnoreCase(request.getMethodValue(), HttpMethod.GET.name()) ||
                    StrUtil.containsIgnoreCase(request.getURI().getPath(), TOKEN)) {
                return chain.filter(exchange);
            }

            log.warn("演示环境不能操作-> {},{}", request.getMethodValue(), request.getURI().getPath());
            response.setStatusCode(HttpStatus.LOCKED);
            return response.setComplete();
        }
    }

    /**
     * @author lw
     * @date 2020/7/22
     * 密码解密工具类
     * <p>
     * 参考 ModifyRequestBodyGatewayFilterFactory 实现
     */
    @Component
    class PasswordDecoderFilter extends BaseAbstractGatewayFilter {

        @Override
        Mono<Void> applys(ServerWebExchange exchange, GatewayFilterChain chain, ServerHttpResponse response, ServerHttpRequest request) {
            // 不是登录请求，直接向下执行
            if (!StrUtil.containsAnyIgnoreCase(request.getURI().getPath(), SecurityConstants.OAUTH_TOKEN_URL)) {
                return chain.filter(exchange);
            }

            // 刷新token，直接向下执行
            String grantType = request.getQueryParams().getFirst("grant_type");
            if (StrUtil.equals(SecurityConstants.REFRESH_TOKEN, grantType)) {
                return chain.filter(exchange);
            }

            ServerRequest serverRequest = ServerRequest.create(exchange, messageReaders);

            // 解密生成新的报文
            Mono<?> modifiedBody = serverRequest.bodyToMono(String.class).flatMap(decryptAES());

            Class outClass = String.class;
            HttpHeaders headers = new HttpHeaders();
            headers.putAll(exchange.getRequest().getHeaders());
            headers.remove(HttpHeaders.CONTENT_LENGTH);
            headers.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE);

            CachedBodyOutputMessage outputMessage = new CachedBodyOutputMessage(exchange, headers);

            return BodyInserters.fromPublisher(modifiedBody, outClass).insert(outputMessage, new BodyInserterContext())
                    .then(Mono.defer(() -> chain.filter(exchange.mutate().request(decorate(exchange, headers, outputMessage)).build()))
                    );
        }
    }

    /**
     * @author lw
     * @date 2020/7/22
     * 验证码处理
     */
    @Slf4j
    @Component
    static class ValidateCodeGatewayFilter extends BaseAbstractGatewayFilter {

        @Override
        Mono<Void> applys(ServerWebExchange exchange, GatewayFilterChain chain, ServerHttpResponse response, ServerHttpRequest request) {
            // 不是登录请求，直接向下执行
            if (!StrUtil.containsAnyIgnoreCase(request.getURI().getPath()
                    , SecurityConstants.OAUTH_TOKEN_URL, SecurityConstants.SMS_TOKEN_URL
                    , SecurityConstants.SOCIAL_TOKEN_URL)) {
                return chain.filter(exchange);
            }

            // 刷新token，直接向下执行
            String grantType = request.getQueryParams().getFirst("grant_type");
            if (StrUtil.equals(SecurityConstants.REFRESH_TOKEN, grantType)) {
                return chain.filter(exchange);
            }

            // 终端设置不校验， 直接向下执行
            try {
                String header = request.getHeaders().getFirst(HttpHeaders.AUTHORIZATION);
                String clientId = WebUtils.getClientId(header);
                if (filterIgnorePropertiesConfig.getClients().contains(clientId)) {
                    return chain.filter(exchange);
                }

                // 如果是社交登录，判断是否包含SMS
                if (StrUtil.containsAnyIgnoreCase(request.getURI().getPath(), SecurityConstants.SOCIAL_TOKEN_URL)) {
                    String mobile = request.getQueryParams().getFirst("mobile");
                    if (StrUtil.containsAny(mobile, LoginTypeEnum.SMS.getType())) {
                        throw new ValidateCodeException("验证码不合法");
                    } else {
                        return chain.filter(exchange);
                    }
                }

                //校验验证码
                checkCode(request);
            } catch (Exception e) {
                response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
                response.setStatusCode(HttpStatus.PRECONDITION_REQUIRED);
                try {
                    return response.writeWith(
                            Mono.just(response.bufferFactory()
                                    .wrap(objectMapper.writeValueAsBytes(R.failed(e.getMessage())))));
                } catch (JsonProcessingException e1) {
                    log.error("对象输出异常", e1);
                }
            }

            return chain.filter(exchange);
        }

    }

}
